package ly.count.ohos.sdk;

import ohos.hiviewdfx.HiLog;
import ohos.hiviewdfx.HiLogLabel;

import java.io.PrintWriter;
import java.io.StringWriter;
import java.net.UnknownHostException;

/**
 * 日志工具类
 *
 * @since 2021-04-07
 */
public class ModuleLog {
    /**
     * 日志回调接口
     *
     * @since 2021-04-07
     */
    public interface LogCallback {
        /**
         * 日志出发回调
         *
         * @param logMessage
         * @param logLevel
         */
        void logHappened(String logMessage, ModuleLog.LogLevel logLevel);
    }

    private static final HiLogLabel LABEL = new HiLogLabel(HiLog.LOG_APP, 0x00201, Countly.TAG);
    /**
     * 日志级别
     *
     * @since 2021-04-07
     */
    public enum LogLevel {
        /**
         * Verbose
         */
        Verbose,

        /**
         * Debug
         */
        Debug,

        /**
         * Info
         */
        Info,

        /**
         * Warning
         */
        Warning,

        /**
         * Error
         */
        Error
    }

    LogCallback logListener = null;

    ModuleLog() {
    }

    void setListener(LogCallback logCallback) {
        this.logListener = logCallback;
    }

    /**
     * v
     *
     * @param msg
     */

    public void v(String msg) {
        if (!logEnabled()) {
            return;
        }
        if (Countly.sharedInstance().isLoggingEnabled()) {
            HiLog.info(LABEL, msg);
        }
        informListener(msg, null, LogLevel.Verbose);
    }

    /**
     * d
     *
     * @param msg
     */
    public void d(String msg) {
        if (!logEnabled()) {
            return;
        }
        if (Countly.sharedInstance().isLoggingEnabled()) {
            HiLog.debug(LABEL, msg);
        }
        informListener(msg, null, LogLevel.Debug);
    }

    /**
     * i
     *
     * @param msg
     */
    public void i(String msg) {
        if (!logEnabled()) {
            return;
        }
        if (Countly.sharedInstance().isLoggingEnabled()) {
            HiLog.info(LABEL, msg);
        }
        informListener(msg, null, LogLevel.Info);
    }

    /**
     * w
     *
     * @param msg
     */
    public void w(String msg) {
        w(msg, null);
    }

    /**
     * w
     *
     * @param msg
     * @param t
     */
    public void w(String msg, Throwable t) {
        if (!logEnabled()) {
            return;
        }
        if (Countly.sharedInstance().isLoggingEnabled()) {
            HiLog.warn(LABEL, msg);
        }
        informListener(msg, null, LogLevel.Warning);
    }

    /**
     * e
     *
     * @param msg
     */
    public void e(String msg) {
        e(msg, null);
    }

    /**
     * e
     *
     * @param msg
     * @param t
     */
    public void e(String msg, Throwable t) {
        if (!logEnabled()) {
            return;
        }
        if (Countly.sharedInstance().isLoggingEnabled()) {
            HiLog.error(LABEL, msg);
        }
        informListener(msg, t, LogLevel.Error);
    }

    /**
     * log switch
     *
     * @return 日志开发是否打卡
     */
    public boolean logEnabled() {
        return (logListener != null) || Countly.sharedInstance().isLoggingEnabled();
    }

    private void informListener(String msg, final Throwable t, final LogLevel level) {
        String msg1 = "";
        if (msg != null && msg.length() > 0) {
            msg1 = msg;
        }
        if (t != null) {
            msg1 += getStackTraceString(t);
        }

        if (logListener != null) {
            logListener.logHappened(msg1, level);
        }
    }

    /**
     * Handy function to get (a) loggable stack trace from (a) Throwable
     *
     * @param tr An exception to log
     * @return  String
     *
     */
    private String getStackTraceString(Throwable tr) {
        if (tr == null) {
            return "";
        }

        // This is to reduce the amount of log spew that apps do in the non-error
        // condition of the network being unavailable.
        Throwable throwable = tr;
        while (throwable != null) {
            if (throwable instanceof UnknownHostException) {
                return "";
            }
            throwable = throwable.getCause();
        }

        StringWriter sw = new StringWriter();
        PrintWriter pw = new PrintWriter(sw);
        pw.flush();
        return sw.toString();
    }
}
